#pragma once

#include<iostream>
#include<queue>
#include<pthread.h>
#include"Mutex.hpp"
#include"Cond.hpp"

template<class T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _bq.size() >= _capacity;
    }

    bool IsEmpty()
    {
        return _bq.empty();
    }

public:
    BlockQueue(int capacity = 5)
        :_capacity(capacity)
        ,_p_wait_num(0)
        ,_c_wait_num(0)
    { }

    void Enqueue(const T& in)
    {
        //pthread_mutex_lock(&_lock);
        LockGuard lock_guard(&_lock);
        while(IsFull())
        {
            _p_wait_num++;
            //pthread_cond_wait(&_p_cond, &_lock);
            _p_cond.Wait(_lock);
            _p_wait_num--;
        }
        _bq.push(in);
        if(_c_wait_num > 0)
        {
            //pthread_cond_signal(&_c_cond);
            _c_cond.NotifyOne();
        }
        //pthread_mutex_unlock(&_lock);
    }

    void Pop(T* out)
    {
        //pthread_mutex_lock(&_lock);
        while(IsEmpty())
        {
            _c_wait_num++;
            //pthread_cond_wait(&_c_cond, &_lock);
            _c_cond.Wait(_lock);
            _c_wait_num--;
        }
        *out = _bq.front();
        _bq.pop();
        if(_p_wait_num > 0)
        {
            //pthread_cond_signal(&_p_cond);
            _p_cond.NotifyOne();
        }
        //pthread_mutex_unlock(&_lock);
    }

private:
    std::queue<T> _bq;
    int _capacity;
    Cond _c_cond;
    Cond _p_cond;
    int _p_wait_num;
    int _c_wait_num;
    Mutex _lock;
};